/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package fr.loof.fonzie;

import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.persistence.Id;

import org.springframework.util.ReflectionUtils;

/**
 * @author <a href="nicolas@apache.org">Nicolas De Loof</a>
 */
public class JPAUtils
{
    private JPAUtils()
    {
        super();
    }

    private static final ConcurrentMap<Class, Field> ID_FIELDS = new ConcurrentHashMap<Class, Field>();

    public static Object getId( Object entity )
    {
        Class<?> targetClass = entity.getClass();
        Field f = ID_FIELDS.get( targetClass );
        if ( f == null )
        {
            f = getIdField( targetClass );
            ID_FIELDS.putIfAbsent( targetClass, f );
        }

        return ReflectionUtils.getField( f, entity );
    }

    public static void setId( Object id, Object entity )
    {
        Class<?> targetClass = entity.getClass();
        Field f = ID_FIELDS.get( targetClass );
        if ( f == null )
        {
            f = getIdField( targetClass );
            ID_FIELDS.putIfAbsent( targetClass, f );
        }
        ReflectionUtils.setField( f, entity, id );
    }

    /**
     * @param targetClass
     * @return
     */
    private static Field getIdField( Class<?> targetClass )
    {
        do
        {
            for ( Field field : targetClass.getDeclaredFields() )
            {
                if ( field.getAnnotation( Id.class ) != null )
                {
                	ReflectionUtils.makeAccessible( field );
                    return field;
                }
            }
            targetClass = targetClass.getSuperclass();
        }
        while ( targetClass != null && targetClass != Object.class );
        throw new IllegalArgumentException( "Entity " + targetClass + " has no @Id field" );
    }
}
